<?php
/**
 * File Class
 *
 * @author @@AUTHOR@@
 * @copyright @@AUTHOR@@ @@YEAR@@
 * @version @@VERSION@@
 * @package Extensions
 * @subpackage Low-Cal_Vanilla
 * @license  @@LICENCE_URL@@ @@LICENCE@@
 *
 * $Id: Class.File.php 303 2008-09-28 21:13:41Z dinoboff $
 * $HeadURL$
 */


/**
 * File Class, to get details and content of a file
 * @package Extensions
 * @subpackage Low-Cal_Vanilla
 */
class LowCalVanilla_File {

	/**
	 * @var string
	 */
	var $BaseName;

	/**
	 * @var string
	 */
	var $Body;

	/**
	 * @var string
	 */
	var $DirName;

	/**
	 * @var string
	 */
	var $Extension;

	/**
	 * @var string
	 */
	var $FileName;

	/**
	 * @var resource
	 */
	var $Fp;

	/**
	 * @var string
	 */
	var $Path;

	/**
	 * @var int;
	 */
	var $MaxLength = 0;

	/**
	 * @var int
	 */
	var $Size;

	/**
	 * @var array
	 */
	var $Options;

	/**
	 * @var Context
	 */
	var $Context;

	/**
	 * Constructor
	 *
	 * @param Context $Context
	 * @param string $Path
	 * @param string $Mode See fopen documentation. Will open the stream if set
	 * @param array $Options See NewFile::SetOptions
	 * @return NewFile
	 */
	function LowCalVanilla_File(&$Context, $Path, $Mode='', $Options=array()) {
		$this->Context = & $Context;
		$this->SetOptions($Options);
		$this->Path = $Path;
		if ($Mode) {
			$this->Open($Mode);
		}
		if (version_compare(phpversion(), '5.0.0', '<')) {
			$this->MaxLength = 4096;
		}
	}

	/**
	 * Closes file handler stream.
	 *
	 * @return boolean
	 */
	function Close() {
		$Return = false;
		if ($this->IsOpen()) {
			$Return = @fclose($this->Fp);
			$this->Fp = null;
		}
		return $Return;
	}

	/**
	 * Tests for end-of-file on a file pointer.
	 *
	 * @return unknown
	 */
	function Eof() {
		return @feof( $this->Fp );
	}

	/**
	 * Tests if the file exist.
	 *
	 * @return boolean
	 */
	function Exist() {
		return file_exists($this->Path);
	}

	/**
	 * Forces write buffered output to file.
	 *
	 * @return boolean
	 */
	function Flush() {
		return @fflush( $this->Fp );
	}

	/**
	 * Reads entire file into a string.
	 *
	 * @return string
	 */
	function GetContents($KeepOpened = false) {
		if (!$this->IsOpen()) {
			$this->Open('rb');
		}
		$Pos = $this->Tell();
		$this->Rewind();
		$this->Body = '';
		while ($Line = $this->Gets()) {
			$this->Body .= $Line;
		}
		$this->GetSize();
		if ($KeepOpened) {
			$this->Seek($Pos);
		} else {
			$this->Close();
		}
		return $this->Body;
	}

	/**
	 * Reads entire file into an array.
	 *
	 * @return array
	 */
	function GetContentsInArray() {
		$Pos = $this->Tell();
		$this->Rewind();
		$this->Body = array();
			while ($Line = $this->Gets()) {
			$this->Body[] = $Line;
		}
		$this->GetSize();
		$this->Seek($Pos);
		return $this->Body;
	}

	/**
	 * Gets character from file pointer.
	 *
	 * @return string
	 */
	function Getc() {
		return @fgetc( $this->Fp );
	}

	/**
	 * Return last modification in Unix timestamp
	 *
	 * @return int
	 */
	function GetLastModification() {
		return @filemtime($this->Path);
	}

	/**
	 * Returns information about a file path.
	 *
	 * @return array
	 */
	function GetPathInfo() {
		$PathInfo = pathinfo($this->Path);
		$this->DirName = $PathInfo['dirname'];
		$this->BaseName = $PathInfo['basename'];
		$this->Extension = $PathInfo['extension'];
		if (array_key_exists('filename', $PathInfo)) { // only since phh 5.2.0
			$this->FileName = $PathInfo['filename'];
		} else if ($PathInfo['extension']) {
			$this->FileName = substr_replace(
				$PathInfo['basename'], '', - ( strlen( $PathInfo['extension'] ) + 1 ));
		} else {
			$this->FileName = '';
		}
		return $PathInfo;
	}

	/**
	 * Gets a line from file pointer.
	 *
	 * @return string
	 */
	function Gets() {
		if ($this->MaxLength) {
			return @fgets($this->Fp, $this->MaxLength);
		} else {
			return @fgets($this->Fp);
		}
	}

	/**
	 * Gets file size.
	 *
	 * @return int
	 */
	function GetSize() {
		$this->Size = filesize($this->Path);
		return $this->Size;
	}

	/**
	 * Check if the file has changed
	 *
	 * @param int $Time Unix timestamp
	 * @return boolean
	 */
	function HasChanged($Time) {
		return $this->GetLastModification() !== (int) $Time;
	}

	/**
	 * Tests is the file is readable.
	 *
	 * @return boolean
	 */
	function IsReadable() {
		return (is_file($this->Path) && is_readable($this->Path));
	}

	/**
	 * Check if a stream to the file is open
	 *
	 * @return unknown
	 */
	function IsOpen() {
		return is_resource($this->Fp);
	}

	/**
	 * Check the file is writable
	 *
	 * @return boolean
	 */
	function IsWritable() {
		return (is_file($this->Path) && is_writable($this->Path));
	}

	/**
	 * Locks a file.
	 *
	 * See flock
	 *
	 * @param int $Operation
	 * @param boolean $WouldBlock
	 * @return boolean
	 */
	function Lock($Operation) {
		$Lock = @flock( $this->Fp, $Operation );
		if (!$Lock) {
			$this->Context->ErrorManager->AddError(
					$this->Context, 'NewFile', 'Lock', 'Could not get lock ' . $this->Path,
					'2', $this->Options['KillOnFail']);
		}
		return $Lock;
	}

	/**
	 * Open stream
	 *
	 * @param string $Mode see fopen()
	 * @return boolean
	 */
	function Open($Mode='r') {
		$this->Close();
		$this->Fp = @fopen(
			$this->Path, $Mode, $this->Options['UseIncludePath']);

		if (!$this->Fp) {
			$this->Context->ErrorManager->AddError(
				$this->Context, 'NewFile', 'NewFile',
				'Could not open ' . $this->Path,
				'1', $this->Options['KillOnFail']);
		} else if ($this->Options['UseLock']) {
			if (strpos($Mode, 'w') !== false
				|| strpos($Mode, 'a') !== false
				|| strpos($Mode, '+') !== false
			) {
				$Lock = $this->Lock(LOCK_EX | LOCK_NB);
			} else {
				$Lock = $this->Lock(LOCK_SH | LOCK_NB);
			}
			if (!$Lock) {
				$this->Close();
			}
		}
		return $this->IsOpen();
	}


	/**
	 * Outputs all remaining data on a file pointer.
	 *
	 * @return string
	 */
	function PassThru() {
		return @fpassthru( $this->Fp );
	}

	/**
	 *
	 */
	function Read($Length) {
		return @fread($this->Fp, $Length);
	}

	/**
	 *
	 */
	function ReadThru() {
		return @fread($this->Fp, $this->GetSize());
	}

	/**
	 * Resets file pointer.
	 *
	 * @return boolean
	 */
	function Rewind() {
		return @rewind($this->Fp);
	}

	/**
	 * Seeks on a file pointer.
	 *
	 * See fseek()
	 *
	 * @param int $Pos
	 * @param int $Whence
	 * @return unknown
	 */
	function Seek($Pos, $Whence=SEEK_SET) {
		return @fseek( $this->Fp, $Pos, $Whence );
	}

	function SetContents($Body) {
		$this->Body = $Body;
		$this->Close();
		$this->Open('wb');
		return $this->Write($Body);
	}


	/**
	 * Set default options when not set
	 *
	 * @param array $Options
	 */
	function SetOptions($Options) {
		$DefaultOptions = array(
			'UseIncludePath' => false,
			'UseLock' => true,
			'KillOnFail' => false
		);
		$this->Options = array_merge($DefaultOptions, $Options);
	}

	/**
	 * Gets information about a file using an open file pointer.
	 *
	 * @return array
	 */
	function Stat() {
		return @fstat( $this->Fp );
	}

	/**
	 * Returns the current position of the file read/write pointer
	 *
	 * @return int
	 */
	function Tell() {
		return @ftell( $this->Fp );
	}

	/**
	 * Truncates a file to a given length
	 *
	 * @param int $Size
	 * @return boolean
	 */
	function Truncate($Size) {
		return (boolean) @ftruncate( $this->Fp, $Size );
	}

	/**
	 * Remove file
	 *
	 * @return boolean
	 */
	function Unlink() {
		$this->Close();
		return @unlink($this->Path);
	}

	/**
	 * Binary-safe file write.
	 *
	 * @param string $Str
	 * @param int $Length
	 * @return boolean
	 */
	function Write($Str) {
		return @fwrite( $this->Fp, $Str );
	}

	/**
	 * Convert an instance to string (php 5.2.0), using the current line.
	 *
	 * @return string
	 */
	function __toString() {
		return $this->Gets();
	}

	/**
	 * Close stream when the instance is destroyed (php 5).
	 *
	 */
	function __destruct() {
		$this->Close();
	}

	/**
	 * Close stream when the object is serialized (php 5).
	 *
	 */
	function __sleep() {
		$this->Close();
	}
}